// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: jhkim

#pragma once

#include <set>
#include <algorithm>
#include "coin2/strategy/order_executor/executor.h"
#include "coin2/strategy/linear_model/calc_util.h"
#include "coin2/strategy/linear_model/working_order_manager.h"
#include "presto/quant/base/time.h"

namespace coin2::strategy::linear_model {

using coin::proto::OrderDirection;
using coin::proto::OrderDuration;
using coin::proto::OrderType;

struct Bbo {
  int64_t fetched_time;
  int64_t exchange_time;
  double askp;
  double bidp;
};

struct BlockOrderMeasures {
  explicit BlockOrderMeasures(int64_t strat_init_time)
      : strat_init_time(strat_init_time) {}
  int64_t prev_ask_post_time = 0L;
  int64_t prev_bid_post_time = 0L;
  int64_t prev_ask_fill_time = 0L;
  int64_t prev_bid_fill_time = 0L;
  int64_t prev_buy_taker_time = 0L;
  int64_t prev_sell_taker_time = 0L;
  const int64_t strat_init_time = 0L;
};

inline double Addbp(double price, double add_bp) {
  return price * (1 + 1e-4 * add_bp);
}


inline void ManageMmOrder(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    int64_t timestamp,

    BlockOrderMeasures* bom,
    double leverage_rate,

    double midret_pred,
    double signal_unskewed_lb,
    double signal_unskewed_ub,
    double signal_skewed_lb,
    double signal_skewed_ub,
    double signal_sigma,
    bool sell_block,
    bool buy_block,

    const LmFocusConfig& fconf) {
  if (bbo.exchange_time > 0 && bbo.fetched_time > bbo.exchange_time + 500e6) {
    oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return;
  }

  double maker_qty_ratio = fconf.maker_qty_ratio();
  double max_lot_size = oe->GetConfig(pholder).qty().max_lot_size();
  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;
  double midp = 0.5 * (ask0p + bid0p);

  auto& tickprc = pholder.product_info().tick_price();
  if (fconf.has_buy_threshold_bps()) {
    buy_block = buy_block || signal_skewed_lb <= fconf.buy_threshold_bps();
    sell_block = sell_block || signal_skewed_ub >= -fconf.buy_threshold_bps();
  }
  if (fconf.has_buy_threshold_nsigma()) {
    buy_block = buy_block || signal_skewed_lb <= (fconf.buy_threshold_nsigma() * signal_sigma);
    sell_block = sell_block || signal_skewed_ub >= (-fconf.buy_threshold_nsigma() * signal_sigma);
  }

  bool allow_buy_fill_cooltime =
      bom->prev_bid_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_fill_cooltime =
      bom->prev_ask_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_taker_cooltime =
      bom->prev_buy_taker_time + fconf.taker_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_taker_cooltime =
      bom->prev_sell_taker_time + fconf.taker_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_post_cooltime =
      bom->prev_bid_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_post_cooltime =
      bom->prev_ask_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_taker_initiate_time =
      bom->strat_init_time + fconf.taker_warmup_sec() * Time::kNanosecondsPerSecond < timestamp;

  bool allow_buy_taker = allow_buy_fill_cooltime && allow_buy_taker_cooltime && allow_taker_initiate_time;
  bool allow_sell_taker = allow_sell_fill_cooltime && allow_sell_taker_cooltime && allow_taker_initiate_time;
  bool allow_buy_maker = allow_buy_fill_cooltime && allow_buy_post_cooltime;
  bool allow_sell_maker = allow_sell_fill_cooltime && allow_sell_post_cooltime;

  // threshold_bps cant be zero. will be waste of money
  if (fconf.has_threshold_bps() && fconf.threshold_bps() > 0) {
    double spdbps = 1e4 * (ask0p - bid0p) / midp;
    const double agg_threshold_bps = fconf.threshold_bps() + fconf.threshold_bps_add();
    const double hurdle_bps = agg_threshold_bps + 0.5 * spdbps;
    if (signal_unskewed_lb > hurdle_bps && allow_buy_taker) {
      double extra_signal = signal_unskewed_lb - hurdle_bps;
      double buy_aggp = ask0p * (1 + extra_signal * 1e-4);
      bom->prev_buy_taker_time = timestamp;
      oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(buy_aggp, OrderDirection::DIRECTION_BUY),
          max_lot_size,
          OrderDirection::DIRECTION_BUY,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          ask0p,
          leverage_rate,
          false,  // post_only
          "taker",
          timestamp,
          false,
          false);
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      return;
    } else if (-signal_unskewed_ub > hurdle_bps && allow_sell_taker) {
      double extra_signal = -signal_unskewed_ub - hurdle_bps;
      double sell_aggp = bid0p * (1 - extra_signal * 1e-4);
      bom->prev_sell_taker_time = timestamp;
      oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(sell_aggp, OrderDirection::DIRECTION_SELL),
          max_lot_size,
          OrderDirection::DIRECTION_SELL,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          bid0p,
          leverage_rate,
          false,  // post_only
          "taker",
          timestamp,
          false,
          false);
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      return;
    }
  }

  const auto* ask_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_SELL);
  const auto* bid_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_BUY);

  double ask_edge_bps = fconf.edge_bps();
  double bid_edge_bps = fconf.edge_bps();
  bool ask_post = !sell_block;
  bool bid_post = !buy_block;

  if (bid_post && midret_pred > fconf.opposite_side_threshold_bps()) {
    if (fconf.cancel_opposite_side()) {
      ask_post = false;
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    } else {
      ask_edge_bps += fconf.opposite_side_edge_bps_add();
    }
  }
  if (ask_post && midret_pred < -fconf.opposite_side_threshold_bps()) {
    if (fconf.cancel_opposite_side()) {
      bid_post = false;
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    } else {
      bid_edge_bps += fconf.opposite_side_edge_bps_add();
    }
  }

  double ask_post_price = std::max(
      Addbp(ask0p, fconf.bbo_inside_bps()),
      Addbp(midp, signal_skewed_ub + ask_edge_bps));

  double ask_inside_bound = Addbp(ask_post_price, -fconf.sticky_inside_bps());
  double ask_outside_bound = Addbp(ask0p, fconf.bbo_outside_bps());
  double ask_sticky_outside = Addbp(ask_post_price, fconf.sticky_outside_bps());

  if (ask_post && ask_inside_order) {
    ask_post = false;
    double ask_working_price = ask_inside_order->order_price();
    if (ask_working_price < ask_inside_bound) {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ask_post = true;
    } else if (ask_working_price > ask_outside_bound) {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ask_post = true;
    } else if (ask_working_price > ask_sticky_outside) {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ask_post = true;
    }
  }

  if (ask_post && allow_sell_maker) {
    bom->prev_ask_post_time = timestamp;
    ask_post_price = std::max(
        ask_post_price,
        tickprc.GetNextPrice(bid0p));

    if (ask_post_price <= ask_outside_bound) {
      double rounded_price =
          tickprc.RoundPassively(
              ask_post_price, OrderDirection::DIRECTION_SELL);
      double raw_qty = max_lot_size * maker_qty_ratio;
      if (raw_qty > 0) {
        oe->SubmitOrder(
            pholder,
            rounded_price,
            raw_qty,
            OrderDirection::DIRECTION_SELL,
            OrderDuration::GTC_ORDER,
            OrderType::LIMIT_ORDER,
            ask0p,
            leverage_rate,
            true,  // post_only
            "passive_post",
            timestamp,
            false,
            false);
      }
    }
  }

  double bid_post_price = std::min(
      Addbp(bid0p, -fconf.bbo_inside_bps()),
      Addbp(midp, signal_skewed_lb - bid_edge_bps));

  double bid_inside_bound = Addbp(bid_post_price, fconf.sticky_inside_bps());
  double bid_outside_bound = Addbp(bid0p, -fconf.bbo_outside_bps());
  double bid_sticky_outside = Addbp(bid_post_price, -fconf.sticky_outside_bps());
  if (bid_post && bid_inside_order) {
    bid_post = false;
    double bid_working_price = bid_inside_order->order_price();
    if (bid_working_price > bid_inside_bound) {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      bid_post = true;
    } else if (bid_working_price < bid_outside_bound) {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      bid_post = true;
    } else if (bid_working_price < bid_sticky_outside) {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      bid_post = true;
    }
  }
  if (bid_post && allow_buy_maker) {
    bom->prev_bid_post_time = timestamp;
    bid_post_price = std::min(
        bid_post_price,
        tickprc.GetPrevPrice(ask0p));

    if (bid_post_price >= bid_outside_bound) {
      double rounded_price =
          tickprc.RoundPassively(
              bid_post_price, OrderDirection::DIRECTION_BUY);
      double raw_qty = max_lot_size * maker_qty_ratio;
      if (raw_qty > 0) {
        oe->SubmitOrder(
            pholder,
            rounded_price,
            raw_qty,
            OrderDirection::DIRECTION_BUY,
            OrderDuration::GTC_ORDER,
            OrderType::LIMIT_ORDER,
            bid0p,
            leverage_rate,
            true,  // post_only
            "passive_post",
            timestamp,
            false,
            false);
      }
    }
  }
}

inline int ManageMmOrder2(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    const int64_t timestamp,
    BlockOrderMeasures* bom,
    const double leverage_rate,

    const double signal_skewed_lb,
    const double signal_skewed_ub,
    const double volatility,

    const bool buy_block,
    const bool sell_block,

    const LmFocusMidfreqConfig& fconf) {
  // return value: number of transactions
  int ret = 0;
  if (bbo.exchange_time > 0 && bbo.fetched_time > bbo.exchange_time + 500e6) {
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return ret;
  }

  // code for order_post_cooltime
  bool allow_buy_post_cooltime =
    bom->prev_bid_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_post_cooltime =
    bom->prev_ask_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;

  double max_lot_size = oe->GetConfig(pholder).qty().max_lot_size();
  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;
  double min_bbo_dist_bps = std::max(fconf.min_bbo_dist_bps(),
      volatility * fconf.volat_const());
  min_bbo_dist_bps = std::min(fconf.max_bbo_dist_bps(), min_bbo_dist_bps);

  // // TODO(dhjang): seperate original manage_order part & 30min_exec_order part

  auto& tickprc = pholder.product_info().tick_price();
  // threshold_bps cant be zero. will be waste of money
  if (signal_skewed_lb > fconf.buy_threshold_bps() && !buy_block) {
    double pass_price = bid0p * (1 - fconf.relax_post_bps() * 1e-4);
    pass_price = std::min(ask0p * (1 - min_bbo_dist_bps * 1e-4), pass_price);
    // case of hedge_signal
    if (signal_skewed_lb > 500) {
      pass_price = std::min(ask0p * (1 - 0.5 * 1e-4), bid0p);
    }
    const auto* bid_inside_order =
      oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_BUY);
    if (bid_inside_order) {
      double bid_working_price = bid_inside_order->order_price();
      if (std::abs(pass_price - bid_working_price) < fconf.sticky_bps() * 1e-4 * bid_working_price)
        return ret;
      double tick_size = tickprc.GetNthNextPrice(pass_price, 2) - tickprc.GetNthNextPrice(pass_price, 1);
      if (std::abs(pass_price - bid_working_price) < tick_size * fconf.sticky_ticks() * (1 + 1e-4))
        return ret;
    }
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    if (allow_buy_post_cooltime) {
      auto oe_res = oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_BUY),
          max_lot_size,
          OrderDirection::DIRECTION_BUY,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_BUY),
          leverage_rate,
          true,  // post_only
          "maker",
          timestamp,
          false,
          false,
          std::nullopt,
          std::nullopt,
          true);
      if (oe_res.qty > 0) {
        bom->prev_bid_post_time = timestamp;
        ret++;
      }
    }
  } else if (signal_skewed_ub < fconf.sell_threshold_bps() && !sell_block) {
    double pass_price = ask0p * (1 + fconf.relax_post_bps() * 1e-4);
    pass_price = std::max(bid0p * (1 + min_bbo_dist_bps * 1e-4), pass_price);
    // case of hedge_signal
    if (signal_skewed_ub < -500) {
      pass_price = std::max(bid0p * (1 + 0.5 * 1e-4), ask0p);
    }
    const auto* ask_inside_order =
      oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_SELL);
    if (ask_inside_order) {
      double ask_working_price = ask_inside_order->order_price();
      if (std::abs(pass_price - ask_working_price) < fconf.sticky_bps() * 1e-4 * ask_working_price)
        return ret;
      // add sticky_ticks for high_spread symbol(especially upbit, bithumb)
      double tick_size = tickprc.GetNthNextPrice(pass_price, 2) - tickprc.GetNthNextPrice(pass_price, 1);
      if (std::abs(pass_price - ask_working_price) < tick_size * fconf.sticky_ticks() * (1 + 1e-4))
        return ret;
    }
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    if (allow_sell_post_cooltime) {
      auto oe_res = oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_SELL),
          max_lot_size,
          OrderDirection::DIRECTION_SELL,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          tickprc.RoundPassively(ask0p, OrderDirection::DIRECTION_SELL),
          leverage_rate,
          true,  // post_only
          "maker",
          timestamp,
          false,
          false,
          std::nullopt,
          fconf.longonly(),  // reduce_only
          !fconf.longonly());
      if (oe_res.qty > 0) {
        bom->prev_ask_post_time = timestamp;
        ret++;
      }
    }
  }
  return ret;
}

inline int ManageMmOrder3(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    const int64_t timestamp,
    BlockOrderMeasures* bom,
    const double leverage_rate,
    const double signal_skewed_lb,
    const double signal_skewed_ub,
    const double sigma,
    const double buy_threshold_bps,
    const double sell_threshold_bps,
    const bool buy_block,
    const bool sell_block,
    const LmFocusMidfreqConfig& fconf) {
  // return value: number of transactions
  int ret = 0;
  if (bbo.fetched_time > bbo.exchange_time + 500e6) {
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return ret;
  }
  // code for order_post_cooltime
  bool allow_buy_post_cooltime =
    bom->prev_bid_post_time + fconf.order_cool_in_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_post_cooltime =
    bom->prev_ask_post_time + fconf.order_cool_in_sec() * Time::kNanosecondsPerSecond < timestamp;

  std::set<int64_t> cancel_list;
  double max_lot_size = oe->GetConfig(pholder).qty().max_lot_size();
  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;
  double midp = (ask0p + bid0p) / 2.0;
  int64_t cancel_cooldown = fconf.cancel_cooldown_sec() * 1'000'000'000LL;

  // cancel old orders
  for (const auto* order : oe->GetAllProductWorkingOrders(pholder)) {
    if (order->order_created_time() + fconf.max_posting_period() * 1'000'000'000LL < timestamp) {
      cancel_list.insert(order->proc_order_id());
    }
  }

  // to decide order price
  auto& tickprc = pholder.product_info().tick_price();
  double edge_bps = std::max(0.0, sigma * static_cast<double>(fconf.nsigma_edge()));

  // buy order
  if ((signal_skewed_lb >= buy_threshold_bps) && !buy_block) {
    double pass_price = midp * (1 + signal_skewed_lb * 1e-4) * (1 - edge_bps * 1e-4);
    pass_price = std::min(ask0p * (1 - fconf.min_bbo_dist_bps() * 1e-4), pass_price);
    pass_price = std::min(bid0p, pass_price);
    // case of hedge_signal
    if (signal_skewed_lb > 500) {
      pass_price = std::min(ask0p * (1 - 0.5 * 1e-4), bid0p);
    }

    bool stick = false;
    for (const auto* order : oe->GetAllProductWorkingOrders(pholder)) {
      if (order->direction() != OrderDirection::DIRECTION_BUY) continue;
      if (cancel_list.count(order->proc_order_id())) continue;
      if (std::abs(order->order_price() - pass_price) < pass_price * fconf.sticky_bps() * 1e-4) {
        stick = true;
      }
      if (std::abs(order->order_price() - pass_price) > fconf.push_bps() * pass_price * 1e-4
          && order->order_created_time() + cancel_cooldown < timestamp) {
        cancel_list.insert(order->proc_order_id());
      }
    }
    if (bid0p - pass_price < pass_price * fconf.thrift_bps() * 1e-4 && !stick) {
      if (allow_buy_post_cooltime) {
        auto oe_res = oe->SubmitOrder(
            pholder,
            tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_BUY),
            max_lot_size,
            OrderDirection::DIRECTION_BUY,
            OrderDuration::GTC_ORDER,
            OrderType::LIMIT_ORDER,
            tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_BUY),
            leverage_rate,
            true,  // post_only
            "maker",
            timestamp,
            false,
            false);

        if (oe_res.qty > 0) {
          bom->prev_bid_post_time = timestamp;
          ret++;
        }
      }
    }
  }

  // sell order
  if ((signal_skewed_ub <= sell_threshold_bps) && !sell_block) {
    double pass_price = midp * (1 + signal_skewed_ub * 1e-4) * (1 + edge_bps * 1e-4);
    pass_price = std::max(bid0p * (1 + fconf.min_bbo_dist_bps() * 1e-4), pass_price);
    pass_price = std::max(ask0p, pass_price);
    // case of hedge_signal
    if (signal_skewed_ub < -500) {
      pass_price = std::max(bid0p * (1 + 0.5 * 1e-4), ask0p);
    }

    bool stick = false;
    for (const auto* order : oe->GetAllProductWorkingOrders(pholder)) {
      if (order->direction() != OrderDirection::DIRECTION_SELL) continue;
      if (cancel_list.count(order->proc_order_id())) continue;
      if (std::abs(order->order_price() - pass_price) < pass_price * fconf.sticky_bps() * 1e-4) {
        stick = true;
      }
      if (std::abs(order->order_price() - pass_price) > fconf.push_bps() * pass_price * 1e-4
          && order->order_created_time() + cancel_cooldown < timestamp) {
        cancel_list.insert(order->proc_order_id());
      }
    }
    if (pass_price - ask0p < pass_price * fconf.thrift_bps() * 1e-4 && !stick) {
      if (allow_sell_post_cooltime) {
        auto oe_res = oe->SubmitOrder(
            pholder,
            tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_SELL),
            max_lot_size,
            OrderDirection::DIRECTION_SELL,
            OrderDuration::GTC_ORDER,
            OrderType::LIMIT_ORDER,
            tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_SELL),
            leverage_rate,
            true,  // post_only
            "maker",
            timestamp,
            false,
            false);
        if (oe_res.qty > 0) {
          bom->prev_ask_post_time = timestamp;
          ret++;
        }
      }
    }
  }

  for (auto order_id : cancel_list)
    if (oe->CancelOrder(pholder, timestamp, order_id)) ret++;

  return ret;
}

inline int ManageMmOrder4(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    int64_t timestamp,

    BlockOrderMeasures* bom,
    double leverage_rate,

    double signal_skewed_lb,
    double signal_skewed_ub,
    double signal_sigma,

    std::optional<double> midtomax_pred,
    std::optional<double> midtomin_pred,

    bool buy_block,
    bool sell_block,

    const LmFocusConfig& fconf,
    
    double rate_used_ratio=0) {
  // return value: number of transactions
  int ret = 0;
  if (bbo.exchange_time > 0 && bbo.fetched_time > bbo.exchange_time + 500e6) {
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return ret;
  }

  double max_lot_size = oe->GetConfig(pholder).qty().max_lot_size();
  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;
  double midp = 0.5 * (ask0p + bid0p);
  double min_bbo_dist_bps = fconf.min_bbo_dist_bps();
  min_bbo_dist_bps = std::min(fconf.max_bbo_dist_bps(), min_bbo_dist_bps);

  // // TODO(jhkim): for inverse semantic???
  // double min_quote = fconf.min_order_quote();
  // double maker_qty_ratio = fconf.maker_qty_ratio();
  // if (max_lot_size * maker_qty_ratio * ask0p < min_quote) return ret;

  auto& tickprc = pholder.product_info().tick_price();
  double threshold_multiplier = 1;
  if (fconf.enable_api_rate_limit_check() &&
      rate_used_ratio > fconf.reduce_order_above_api_rate() &&
      fconf.reduce_order_multiplier() > 0) {
    threshold_multiplier = fconf.reduce_order_multiplier() / (1 - rate_used_ratio + 0.01);
  }
  buy_block = buy_block || (signal_skewed_lb <= fconf.buy_threshold_bps() * threshold_multiplier);
  sell_block = sell_block || (signal_skewed_ub >= -fconf.buy_threshold_bps() * threshold_multiplier);
  if (fconf.has_buy_threshold_nsigma()) {
    buy_block = buy_block || (signal_skewed_lb <= (fconf.buy_threshold_nsigma() * signal_sigma) * threshold_multiplier);
    sell_block = sell_block || (signal_skewed_ub >= (-fconf.buy_threshold_nsigma() * signal_sigma) * threshold_multiplier);
  }

  if (fconf.use_minmax_block()) {
    buy_block = buy_block || *midtomin_pred <= -fconf.minmax_block_threshold_bps();
    sell_block = sell_block || *midtomax_pred >= fconf.minmax_block_threshold_bps();
  }

  bool allow_buy_fill_cooltime =
      bom->prev_bid_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_fill_cooltime =
      bom->prev_ask_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_taker_cooltime =
      bom->prev_buy_taker_time + fconf.taker_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_taker_cooltime =
      bom->prev_sell_taker_time + fconf.taker_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_post_cooltime =
      bom->prev_bid_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_post_cooltime =
      bom->prev_ask_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_taker_initiate_time =
      bom->strat_init_time + fconf.taker_warmup_sec() * Time::kNanosecondsPerSecond < timestamp;

  bool allow_buy_taker = allow_buy_fill_cooltime && allow_buy_taker_cooltime && allow_taker_initiate_time;
  bool allow_sell_taker = allow_sell_fill_cooltime && allow_sell_taker_cooltime && allow_taker_initiate_time;

  // threshold_bps cant be zero. will be waste of money
  if (fconf.has_threshold_bps() && fconf.threshold_bps() > 0) {
    double spdbps = 1e4 * (ask0p - bid0p) / midp;
    const double agg_threshold_bps = (fconf.threshold_bps() + fconf.threshold_bps_add()) * threshold_multiplier;
    const double hurdle_bps = agg_threshold_bps + 0.5 * spdbps;
    if (!buy_block && signal_skewed_lb > hurdle_bps && allow_buy_taker) {
      double extra_signal = signal_skewed_lb - hurdle_bps;
      double buy_aggp = ask0p * (1 + extra_signal * 1e-4);
      bom->prev_buy_taker_time = timestamp;
      auto oe_res = oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(buy_aggp, OrderDirection::DIRECTION_BUY),
          max_lot_size,
          OrderDirection::DIRECTION_BUY,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          ask0p,
          leverage_rate,
          false,  // post_only
          "taker",
          timestamp,
          false,
          false);
      if (oe_res.qty > 0) ret++;
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      return ret;
    } else if (!sell_block && -signal_skewed_ub > hurdle_bps && allow_sell_taker) {
      double extra_signal = -signal_skewed_ub - hurdle_bps;
      double sell_aggp = bid0p * (1 - extra_signal * 1e-4);
      bom->prev_sell_taker_time = timestamp;
      auto oe_res = oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(sell_aggp, OrderDirection::DIRECTION_SELL),
          max_lot_size,
          OrderDirection::DIRECTION_SELL,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          bid0p,
          leverage_rate,
          false,  // post_only
          "taker",
          timestamp,
          false,
          false);
      if (oe_res.qty > 0) ret++;
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      return ret;
    }
  }

  const auto* ask_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_SELL);
  const auto* bid_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_BUY);

  bool bid_post = true, ask_post = true;

  std::optional<double> bid_post_price;
  if (!buy_block) {
    bid_post_price = bid0p * (1 - fconf.relax_post_bps() * 1e-4);
    bid_post_price = std::min(ask0p * (1 - min_bbo_dist_bps * 1e-4), *bid_post_price);
    if (bid_inside_order) {
      double bid_working_price = bid_inside_order->order_price();
      if (std::abs(*bid_post_price - bid_working_price)
          < fconf.sticky_bps() * 1e-4 * bid_working_price) {
        bid_post = false;
      } else {
        ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      }
    }
  } else if (fconf.post_both_side()) {
    bid_post_price = std::min(
        Addbp(bid0p, -fconf.bbo_inside_bps()),
        Addbp(midp, signal_skewed_lb - fconf.edge_bps()));

    double bid_inside_bound = Addbp(*bid_post_price, fconf.sticky_inside_bps());
    double bid_outside_bound = Addbp(bid0p, -fconf.bbo_outside_bps());
    double bid_sticky_outside = Addbp(*bid_post_price, -fconf.sticky_outside_bps());
    if (bid_inside_order) {
      bid_post = false;
      double bid_working_price = bid_inside_order->order_price();
      if (bid_working_price > bid_inside_bound ||
          bid_working_price < bid_outside_bound ||
          bid_working_price < bid_sticky_outside) {
        bid_post = true;
        ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      }
    }
    bid_post_price = std::min(*bid_post_price, tickprc.GetPrevPrice(ask0p));
  }
  if (bid_post_price && bid_post && max_lot_size > 0 && allow_buy_post_cooltime) {
    bom->prev_bid_post_time = timestamp;
    auto oe_res = oe->SubmitOrder(
        pholder,
        tickprc.RoundPassively(*bid_post_price, OrderDirection::DIRECTION_BUY),
        max_lot_size,
        OrderDirection::DIRECTION_BUY,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_BUY),
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false);
    if (oe_res.qty > 0) ret++;
  }

  std::optional<double> ask_post_price;
  if (!sell_block) {
    ask_post_price = ask0p * (1 + fconf.relax_post_bps() * 1e-4);
    ask_post_price = std::max(bid0p * (1 + min_bbo_dist_bps * 1e-4), *ask_post_price);
    if (ask_inside_order) {
      double ask_working_price = ask_inside_order->order_price();
      if (std::abs(*ask_post_price - ask_working_price)
          < fconf.sticky_bps() * 1e-4 * ask_working_price) {
        ask_post = false;
      } else {
        ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      }
    }
  } else if (fconf.post_both_side()) {
    ask_post_price = std::max(
        Addbp(ask0p, fconf.bbo_inside_bps()),
        Addbp(midp, signal_skewed_ub + fconf.edge_bps()));

    double ask_inside_bound = Addbp(*ask_post_price, -fconf.sticky_inside_bps());
    double ask_outside_bound = Addbp(ask0p, fconf.bbo_outside_bps());
    double ask_sticky_outside = Addbp(*ask_post_price, fconf.sticky_outside_bps());
    if (ask_inside_order) {
      ask_post = false;
      double ask_working_price = ask_inside_order->order_price();
      if (ask_working_price < ask_inside_bound ||
          ask_working_price > ask_outside_bound ||
          ask_working_price > ask_sticky_outside) {
        ask_post = true;
        ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      }
    }
    ask_post_price = std::max(*ask_post_price, tickprc.GetNextPrice(bid0p));
  }
  if (ask_post_price && ask_post && max_lot_size > 0 && allow_sell_post_cooltime) {
    bom->prev_ask_post_time = timestamp;
    auto oe_res = oe->SubmitOrder(
        pholder,
        tickprc.RoundPassively(*ask_post_price, OrderDirection::DIRECTION_SELL),
        max_lot_size,
        OrderDirection::DIRECTION_SELL,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        tickprc.RoundPassively(ask0p, OrderDirection::DIRECTION_SELL),
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false);
    if (oe_res.qty > 0) ret++;
  }
  return ret;
}

// this is for manage_order + tracking_target_position
inline int ManageMmOrder5(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    const int64_t timestamp,
    BlockOrderMeasures* bom,
    const double leverage_rate,

    const double signal_skewed_lb,
    const double signal_skewed_ub,
    const double volatility,

    const bool buy_block,
    const bool sell_block,

    const LmFocusMidfreqConfig& fconf,
    const double curr_pos,
    TargetPosWorkingOrderManager* pos_wom_,
    const bool calc_target_pos) {
  // return value: number of transactions
  int ret = 0;
  if (bbo.exchange_time > 0 && bbo.fetched_time > bbo.exchange_time + 500e6) {
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return ret;
  }

  double max_lot_size = oe->GetConfig(pholder).qty().max_lot_size();
  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;
  double midp = (ask0p + bid0p) / 2;
  double min_bbo_dist_bps = std::max(fconf.min_bbo_dist_bps(),
      volatility * fconf.volat_const());
  min_bbo_dist_bps = std::min(fconf.max_bbo_dist_bps(), min_bbo_dist_bps);
  double min_bbo_dist_bps_mid = fconf.min_bbo_dist_bps_mid();
  double relax_post_bps = fconf.relax_post_bps();
  // // TODO(jhkim): for inverse semantic???
  // double min_quote = fconf.min_order_quote();
  // double maker_qty_ratio = fconf.maker_qty_ratio();
  // if (max_lot_size * maker_qty_ratio * ask0p < min_quote) return ret;

  auto& tickprc = pholder.product_info().tick_price();
  std::string symbol_string = pholder.product().relative_norm();
  // threshold_bps cant be zero. will be waste of money
  if (signal_skewed_lb > fconf.buy_threshold_bps() && !buy_block) {
    double pass_price = bid0p * (1 - fconf.relax_post_bps() * 1e-4);
    //pass_price = std::min(ask0p * (1 - min_bbo_dist_bps * 1e-4), pass_price);
    if (relax_post_bps > -2) {
      pass_price = bid0p * (1 - relax_post_bps * 1e-4);
    } else if (min_bbo_dist_bps_mid > -2) {
      pass_price = midp * (1 - min_bbo_dist_bps_mid * 1e-4);
    } else if (min_bbo_dist_bps > -2) {
      pass_price = ask0p * (1 - min_bbo_dist_bps * 1e-4);
    }

    const auto* bid_inside_order =
      oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_BUY);
    if (bid_inside_order) {
      double bid_working_price = bid_inside_order->order_price();
      if (std::abs(pass_price - bid_working_price) < fconf.sticky_bps() * 1e-4 * bid_working_price) {
        return ret;
      }
      double tick_size = tickprc.GetNthNextPrice(pass_price, 2) - tickprc.GetNthNextPrice(pass_price, 1);
      if (std::abs(pass_price - bid_working_price) < tick_size * fconf.sticky_ticks() * (1 + 1e-4)) {
        return ret;
      }
    }
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    auto oe_res = oe->SubmitOrder(
        pholder,
        tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_BUY),
        max_lot_size,
        OrderDirection::DIRECTION_BUY,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_BUY),
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false,
        std::nullopt,
        std::nullopt,
        true);
    if (oe_res.qty > 0) ret++;
    if (calc_target_pos & (oe_res.qty > 0)) {
      pos_wom_->AddSubmitOrder(timestamp, oe_res.first_oid, oe_res.qty);
    }
  } else if (signal_skewed_ub < fconf.sell_threshold_bps() && !sell_block) {
    double pass_price = ask0p * (1 + fconf.relax_post_bps() * 1e-4);
    //pass_price = std::max(bid0p * (1 + min_bbo_dist_bps * 1e-4), pass_price);
    if (relax_post_bps > -2) {
      pass_price = ask0p * (1 + fconf.relax_post_bps() * 1e-4);
    } else if (min_bbo_dist_bps_mid > -2) {
      pass_price = midp * (1 + min_bbo_dist_bps_mid * 1e-4);
    } else if (min_bbo_dist_bps > -2) {
      pass_price = bid0p * (1 + min_bbo_dist_bps * 1e-4);
    }

    const auto* ask_inside_order =
      oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_SELL);
    if (ask_inside_order) {
      double ask_working_price = ask_inside_order->order_price();
      if (std::abs(pass_price - ask_working_price) < fconf.sticky_bps() * 1e-4 * ask_working_price) {
        return ret;
      }
      // add sticky_ticks for high_spread symbol(especially upbit, bithumb)
      double tick_size = tickprc.GetNthNextPrice(pass_price, 2) - tickprc.GetNthNextPrice(pass_price, 1);
      if (std::abs(pass_price - ask_working_price) < tick_size * fconf.sticky_ticks() * (1 + 1e-4)) {
        return ret;
      }
    }
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    auto oe_res = oe->SubmitOrder(
        pholder,
        tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_SELL),
        max_lot_size,
        OrderDirection::DIRECTION_SELL,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        tickprc.RoundPassively(ask0p, OrderDirection::DIRECTION_SELL),
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false,
        std::nullopt,
        fconf.longonly(),  // reduce_only
        !fconf.longonly());
    if (oe_res.qty > 0) ret++;
    if (calc_target_pos & (oe_res.qty > 0)) {
      pos_wom_->AddSubmitOrder(timestamp, oe_res.first_oid, -oe_res.qty);
    }
  }

  // this part is for managing TargetPos order
  if (calc_target_pos) {
    double target_pos = pos_wom_->get_target_position();
    double diff = target_pos - curr_pos;
    if (std::abs(diff) > 0.1 * max_lot_size) {
      if (diff > 0) {
        double lot_size = std::min(diff, max_lot_size);
        double pass_price = bid0p * (1 - fconf.relax_post_bps() * 1e-4);
        // pass_price = std::min(ask0p * (1 - min_bbo_dist_bps * 1e-4), pass_price);
        if (relax_post_bps > -2) {
          pass_price = bid0p * (1 - relax_post_bps * 1e-4);
        } else if (min_bbo_dist_bps_mid > -2) {
          pass_price = midp * (1 - min_bbo_dist_bps_mid * 1e-4);
        } else if (min_bbo_dist_bps > -2) {
          pass_price = ask0p * (1 - min_bbo_dist_bps * 1e-4);
        }
        auto oe_res = oe->SubmitOrder(
            pholder,
            tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_BUY),
            lot_size,
            OrderDirection::DIRECTION_BUY,
            OrderDuration::GTC_ORDER,
            OrderType::LIMIT_ORDER,
            tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_BUY),
            leverage_rate,
            true,  // post_only
            "target",
            timestamp,
            false,
            false,
            std::nullopt,
            std::nullopt,
            true);
        if (oe_res.qty > 0) ret++;
        SPDLOG_WARN(
          "[TIME_ORDER_SUBMIT] symbol: {}, timestamp: {}, order_id: {}, submit_qty: {}, price: {}, target: {}, curr: {}",
          symbol_string, timestamp, oe_res.first_oid, oe_res.qty, pass_price, target_pos, curr_pos);
      } else {
        double lot_size = std::min(-diff, max_lot_size);
        double pass_price = ask0p * (1 + fconf.relax_post_bps() * 1e-4);
        // pass_price = std::max(bid0p * (1 + min_bbo_dist_bps * 1e-4), pass_price);
        if (relax_post_bps > -2) {
          pass_price = ask0p * (1 + fconf.relax_post_bps() * 1e-4);
        } else if (min_bbo_dist_bps_mid > -2) {
          pass_price = midp * (1 + min_bbo_dist_bps_mid * 1e-4);
        } else if (min_bbo_dist_bps > -2) {
          pass_price = bid0p * (1 + min_bbo_dist_bps * 1e-4);
        }

        auto oe_res = oe->SubmitOrder(
            pholder,
            tickprc.RoundPassively(pass_price, OrderDirection::DIRECTION_SELL),
            lot_size,
            OrderDirection::DIRECTION_SELL,
            OrderDuration::GTC_ORDER,
            OrderType::LIMIT_ORDER,
            tickprc.RoundPassively(ask0p, OrderDirection::DIRECTION_SELL),
            leverage_rate,
            true,  // post_only
            "target",
            timestamp,
            false,
            false,
            std::nullopt,
            fconf.longonly(),  // reduce_only
            !fconf.longonly());
        if (oe_res.qty >0) ret++;
        SPDLOG_WARN(
          "[TIME_ORDER_SUBMIT] symbol: {}, timestamp: {}, order_id: {}, submit_qty: {}, price: {}, target: {}, curr: {}",
          symbol_string, timestamp, oe_res.first_oid, -oe_res.qty, pass_price, target_pos, curr_pos);
      }
    }
    pos_wom_->UpdateTimeOnly(timestamp);
  }
  return ret;
}

inline int ManageMmOrder6(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    int64_t timestamp,

    BlockOrderMeasures* bom,
    double leverage_rate,

    std::optional<double> midtomax_pred,
    std::optional<double> midtomin_pred,

    bool buy_block,
    bool sell_block,

    bool need_taker,
    double maker_order_qty,
    double taker_order_qty,
    double posting_dist_bbo,
    double posting_add_tick,

    const LmFocusConfig& fconf) {
  // return value: number of transactions
  int ret = 0;
  if (bbo.exchange_time > 0 && bbo.fetched_time > bbo.exchange_time + 500e6) {
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return ret;
  }

  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;

  // // TODO(jhkim): for inverse semantic???
  // double min_quote = fconf.min_order_quote();
  // double maker_qty_ratio = fconf.maker_qty_ratio();
  // if (max_lot_size * maker_qty_ratio * ask0p < min_quote) return ret;

  auto& tickprc = pholder.product_info().tick_price();

  if (fconf.use_minmax_block()) {
    buy_block = buy_block || *midtomin_pred <= -fconf.minmax_block_threshold_bps();
    sell_block = sell_block || *midtomax_pred >= fconf.minmax_block_threshold_bps();
  }

  bool allow_buy_fill_cooltime =
      bom->prev_bid_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_fill_cooltime =
      bom->prev_ask_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_taker_cooltime =
      bom->prev_buy_taker_time + fconf.taker_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_taker_cooltime =
      bom->prev_sell_taker_time + fconf.taker_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_post_cooltime =
      bom->prev_bid_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_post_cooltime =
      bom->prev_ask_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_taker_initiate_time =
      bom->strat_init_time + fconf.taker_warmup_sec() * Time::kNanosecondsPerSecond < timestamp;

  bool allow_buy_taker = allow_buy_fill_cooltime && allow_buy_taker_cooltime && allow_taker_initiate_time;
  bool allow_sell_taker = allow_sell_fill_cooltime && allow_sell_taker_cooltime && allow_taker_initiate_time;

  if (need_taker) {
    if (!buy_block && allow_buy_taker) {
      double buy_aggp = ask0p * (1.0 + 4.0 * 1e-4); // to do: 4 is too large.
      bom->prev_buy_taker_time = timestamp;
      auto oe_res = oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(buy_aggp, OrderDirection::DIRECTION_BUY),
          taker_order_qty,
          OrderDirection::DIRECTION_BUY,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          ask0p,
          leverage_rate,
          false,  // post_only
          "taker",
          timestamp,
          false,
          false);
      if (oe_res.qty > 0) ret++;
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      return ret;
    } else if (!sell_block && allow_sell_taker) {
      double sell_aggp = bid0p * (1.0 - 4.0 * 1e-4);
      bom->prev_sell_taker_time = timestamp;
      auto oe_res = oe->SubmitOrder(
          pholder,
          tickprc.RoundPassively(sell_aggp, OrderDirection::DIRECTION_SELL),
          taker_order_qty,
          OrderDirection::DIRECTION_SELL,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          bid0p,
          leverage_rate,
          false,  // post_only
          "taker",
          timestamp,
          false,
          false);
      if (oe_res.qty > 0) ret++;
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      return ret;
    }
  }

  const auto* ask_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_SELL);
  const auto* bid_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_BUY);

  bool bid_post = true, ask_post = true;

  std::optional<double> bid_post_price;
  if (!buy_block) {
    bid_post_price = bid0p * (1.0 - fconf.relax_post_bps() * 1e-4);
    if (posting_add_tick) {
      bid_post_price = tickprc.GetNextPrice(bid0p);
    }
    bid_post_price = std::min(ask0p * (1.0 - posting_dist_bbo * 1e-4), *bid_post_price);
    if (bid_inside_order) {
      double bid_working_price = bid_inside_order->order_price();
      if (std::abs(*bid_post_price - bid_working_price)
          < fconf.sticky_bps() * 1e-4 * bid_working_price) {
        bid_post = false;
      } else {
        ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
      }
    }
  }
  if (bid_post_price && bid_post && maker_order_qty > 0.0 && allow_buy_post_cooltime) {
    bom->prev_bid_post_time = timestamp;
    auto oe_res = oe->SubmitOrder(
        pholder,
        tickprc.RoundPassively(*bid_post_price, OrderDirection::DIRECTION_BUY),
        maker_order_qty,
        OrderDirection::DIRECTION_BUY,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        tickprc.RoundPassively(bid0p, OrderDirection::DIRECTION_BUY),
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false);
    if (oe_res.qty > 0) ret++;
  }

  std::optional<double> ask_post_price;
  if (!sell_block) {
    ask_post_price = ask0p * (1.0 + fconf.relax_post_bps() * 1e-4);
    if (posting_add_tick) {
      ask_post_price = tickprc.GetPrevPrice(ask0p);
    }
    ask_post_price = std::max(bid0p * (1.0 + posting_dist_bbo * 1e-4), *ask_post_price);
    if (ask_inside_order) {
      double ask_working_price = ask_inside_order->order_price();
      if (std::abs(*ask_post_price - ask_working_price)
          < fconf.sticky_bps() * 1e-4 * ask_working_price) {
        ask_post = false;
      } else {
        ret += oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
      }
    }
  }
  if (ask_post_price && ask_post && maker_order_qty > 0.0 && allow_sell_post_cooltime) {
    bom->prev_ask_post_time = timestamp;
    auto oe_res = oe->SubmitOrder(
        pholder,
        tickprc.RoundPassively(*ask_post_price, OrderDirection::DIRECTION_SELL),
        maker_order_qty,
        OrderDirection::DIRECTION_SELL,
        OrderDuration::GTC_ORDER,
        OrderType::LIMIT_ORDER,
        tickprc.RoundPassively(ask0p, OrderDirection::DIRECTION_SELL),
        leverage_rate,
        true,  // post_only
        "maker",
        timestamp,
        false,
        false);
    if (oe_res.qty > 0) ret++;
  }
  return ret;
}

inline void ManageClearOrder(
    ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
    const coin2::exchange::base::symbology::IProductHolder& pholder,
    const Bbo& bbo,
    int64_t timestamp,
    double lot_size,
    OrderDirection order_dir,

    BlockOrderMeasures* bom,
    double leverage_rate,

    const LmFocusConfig& fconf) {
  if (bbo.exchange_time > 0 && bbo.fetched_time > bbo.exchange_time + 500e6) {
    oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    return;
  }

  double ask0p = bbo.askp;
  double bid0p = bbo.bidp;
  double midp = 0.5 * (ask0p + bid0p);
  auto& tickprc = pholder.product_info().tick_price();

  bool allow_buy_fill_cooltime =
      bom->prev_bid_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_fill_cooltime =
      bom->prev_ask_fill_time + fconf.fill_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_buy_post_cooltime =
      bom->prev_bid_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;
  bool allow_sell_post_cooltime =
      bom->prev_ask_post_time + fconf.post_cooltime_sec() * Time::kNanosecondsPerSecond < timestamp;

  bool allow_buy_maker = allow_buy_fill_cooltime && allow_buy_post_cooltime;
  bool allow_sell_maker = allow_sell_fill_cooltime && allow_sell_post_cooltime;

  const auto* ask_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_SELL);
  const auto* bid_inside_order = oe->GetInsideOrder(pholder, OrderDirection::DIRECTION_BUY);
  bool ask_post = order_dir == OrderDirection::DIRECTION_SELL;
  double ask_post_price = fconf.post_at_midp_clear() ? midp : ask0p * (1 + fconf.edge_bps_clear() * 1e-4);
  if (ask_inside_order) {
    double ask_working_price = ask_inside_order->order_price();
    if (std::abs(ask_working_price - ask_post_price) < fconf.sticky_bps_clear() * 1e-4 * ask_working_price) {
      ask_post = false;
    } else {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_SELL, timestamp);
    }
  }

  if (ask_post && allow_sell_maker) {
    bom->prev_ask_post_time = timestamp;
    double rounded_price =
        tickprc.RoundPassively(ask_post_price, OrderDirection::DIRECTION_SELL);
    if (lot_size > 0) {
      oe->SubmitOrder(
          pholder,
          rounded_price,
          lot_size,
          OrderDirection::DIRECTION_SELL,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          ask0p,
          leverage_rate,
          true,  // post_only
          "passive_post_clear",
          timestamp,
          false,
          false,
          std::nullopt,
          true); // reduce_only
    }
  }

  bool bid_post = order_dir == OrderDirection::DIRECTION_BUY;
  double bid_post_price = fconf.post_at_midp_clear() ? midp : bid0p * (1 - fconf.edge_bps_clear() * 1e-4);
  if (bid_inside_order) {
    double bid_working_price = bid_inside_order->order_price();
    if (std::abs(bid_working_price - bid_post_price) < fconf.sticky_bps_clear() * 1e-4 * bid_working_price) {
      bid_post = false;
    } else {
      oe->CancelAllOrders(pholder, OrderDirection::DIRECTION_BUY, timestamp);
    }
  }

  if (bid_post && allow_buy_maker) {
    bom->prev_bid_post_time = timestamp;
    double rounded_price =
        tickprc.RoundPassively(bid_post_price, OrderDirection::DIRECTION_BUY);
    if (lot_size > 0) {
      oe->SubmitOrder(
          pholder,
          rounded_price,
          lot_size,
          OrderDirection::DIRECTION_BUY,
          OrderDuration::GTC_ORDER,
          OrderType::LIMIT_ORDER,
          bid0p,
          leverage_rate,
          true,  // post_only
          "passive_post_clear",
          timestamp,
          false,
          false,
          std::nullopt,
          true); // reduce_only
    }
  }

}

inline double sigmoid(double k, double x) {
  return 2 / (1 + std::exp(-k * x)) - 1;
}

inline double logit(double k, double x) {
  double xp = (std::exp(-k) - 0.5) * x + 0.5;
  return std::log(xp) - std::log(1 - xp);
}

// signal to [-1, 1]
// normalizer examples: [-7, -5, -4, -3, 0, 3, 4, 5, 7]
inline double normalizeSignal(double signal, const LmFocusConfig& fconf) {
  static double k = fconf.signal_normalizer();
  static double sigmoidk = sigmoid(-k, 1);
  static double logitk = logit(k, 1);

  signal = std::max(signal, -fconf.signal_bound());
  signal = std::min(signal, fconf.signal_bound());

  signal /= fconf.signal_bound();

  if (k == 0) {
    return signal;
  } else if (k < 0) {
    return sigmoid(-k, signal) / sigmoidk;
  } else {
    return logit(k, signal) / logitk;
  }
}

}  // namespace coin2::strategy::linear_model
